/**
 * 图片浏览器，插件里面的 dom 对象，均为 jquery 封装的 jquery 对象
 */
(function ($) {
    var defaultConfig = {
        /**
         * 获取点击的图片 dom 列表的方法，返回对象是jquery 对象
         */
        getPhotoDoms: function (viewer) {
            return [];
        },
        /**
         * 根据触发预览的图片获取预览图片的url，比如点击的是缩略图，
         * 预览的时候要显示高清大图url是另一个地址，
         * 此时可以重写符合自己要求的业务规则
         * @param {*} anchorImg
         */
        getViewPhotoUrl: function (anchorImg) {
            return $(anchorImg).prop("src");
        },
        /** 遮罩层颜色 */
        maskColor: "#000000",
        /** 是否点击遮罩层也能关闭弹窗 */
        clickMaskHide: true,
        /**放大缩放比例，默认放大和缩小都是 1/4 */
        scaleRate: 0.25,
    };

    var keyCodeEnum = {
        leftArrow: 37,
        rightArrow: 39,
        upArrow: 38,
        downArrow: 40,
    };
    /**获取当前时间 */
    var getNow =
        Date.now ||
        function () {
            return new Date().getTime();
        };

    /**
     * 延时函数
     * @param {*} func
     * @param {*} wait
     * @param {*} args
     */
    var delay = function (func, wait, args) {
        return setTimeout(function () {
            return func.apply(null, args);
        }, wait);
    };

    // Returns a function, that, when invoked, will only be triggered at most once
    // during a given window of time. Normally, the throttled function will run
    // as much as it can, without ever going more than once per `wait` duration;
    // but if you'd like to disable the execution on the leading edge, pass
    // `{leading: false}`. To disable execution on the trailing edge, ditto.

    /**
     * 节流函数
     * @param {*} func
     * @param {*} wait
     * @param {*} options
     */
    var throttle = function (func, wait, options) {
        var timeoutIdx, context, args, result;
        var previous = 0;
        if (!options) {
            options = {};
        }

        //延时执行函数
        var later = function () {
            previous = options.leading === false ? 0 : getNow();
            timeoutIdx = null;
            result = func.apply(context, args);
            if (!timeoutIdx) {
                context = args = null;
            }
        };

        //内部节流函数
        var throttled = function () {
            var nowValue = getNow();

            if (!previous && options.leading === false) {
                previous = nowValue;
            }

            var remaining = wait - (nowValue - previous);
            context = this;
            args = arguments;
            if (remaining <= 0 || remaining > wait) {
                if (timeoutIdx) {
                    clearTimeout(timeoutIdx);
                    timeoutIdx = null;
                }
                previous = nowValue;
                result = func.apply(context, args);
                if (!timeoutIdx) context = args = null;
            } else if (!timeoutIdx && options.trailing !== false) {
                timeoutIdx = setTimeout(later, remaining);
            }
            return result;
        };

        throttled.cancel = function () {
            clearTimeout(timeoutIdx);
            previous = 0;
            timeoutIdx = context = args = null;
        };

        return throttled;
    };

    /**
     * 频繁调用函数不会触发，直到一定时间内没有调用再触发，
     * 或者可以提前触发，然后一定时间内频繁触发都不会调用
     * /

    /**
     * 防抖函数
     * @param {*} func
     * @param {*} wait
     * @param {*} immediate 是否立即调用
     */
    var debounce = function (func, wait, immediate) {
        var timeoutIdx, result;

        //延迟执行函数
        var later = function (context, args) {
            timeoutIdx = null;
            result = func.apply(context, args);
        };

        //防抖内部实现函数
        var debounced = function (args) {
            if (timeoutIdx) {
                clearTimeout(timeoutIdx);
            }
            if (immediate) {
                var callNow = !timeoutIdx;
                timeoutIdx = setTimeout(later, wait);
                if (callNow) {
                    result = func.apply(this, args);
                }
            } else {
                timeoutIdx = delay(later, wait, args);
            }

            return result;
        };

        debounced.cancel = function () {
            clearTimeout(timeoutIdx);
            timeoutIdx = null;
        };

        return debounced;
    };
    var PhotoViewer = function (config, rootDom) {
        PhotoViewer.index += 1;
        PhotoViewer.instances.push(this);
        this.id = PhotoViewer.index;
        this.currentPhotoIndex = 0;
        this.photoDoms = [];
        /**视图面板对象 */
        this.viewerPanel = null;
        this.rootDom = rootDom;
        this.config = $.extend({}, defaultConfig, config);
        /** 是否拖拽过图片,用于处理缩放的时候是否居中定位  */
        this.hasDrag = false;
        this.init();
    };
    PhotoViewer.index = -1;
    PhotoViewer.instances = [];
    PhotoViewer.prototype.init = function () {
        this.reloadPhotoDoms();
    };
    /**
     * 重新加载图片 dom
     */
    PhotoViewer.prototype.reloadPhotoDoms = function () {
        var photoDoms = this.config.getPhotoDoms.apply(this, [this.rootDom]);
        if (photoDoms.length === 0) {
            console.log("没有获取到图片对象");
            return;
        }
        if (photoDoms.jquery === null || photoDoms.jquery === undefined) {
            throw new Error("getPhotoDoms 方法请返回一个jquery 对象数组");
        }

        if (photoDoms.get(0).tagName.toLowerCase() !== "img") {
            throw new Error("数组里面的 dom 对象 不是 image 对象");
        }
        /**
         * 图片鼠标移上去变为手型设置
         */
        photoDoms.css("cursor", "pointer");
        photoDoms.each(function (i, dom) {
            $(this).data("index", i);
        });
        if (this.photoDoms.length > 0) {
            this.photoDoms.off("click");
        }
        this.photoDoms = photoDoms;
        this.registerPhotoDomEvents();
    };
    /**
     * 注册锚点图片的事件
     */
    PhotoViewer.prototype.registerPhotoDomEvents = function () {
        var self = this;
        this.photoDoms.off("click").on("click", function (event) {
            event.stopPropagation();
            var currentPhoto = $(event.currentTarget);
            self.showViewerPanel(currentPhoto);
        });
    };
    PhotoViewer.prototype.showViewerPanel = function (currentPhoto) {
        if (this.viewerPanel === null) {
            this._initViewerPanel();
        }
        this.viewerPanel.show();
        this.currentPhotoIndex = currentPhoto.data("index");
        var viewPhotoUrl = this.config.getViewPhotoUrl.apply(this, [
            currentPhoto,
        ]);
        this._setViewPhoto(viewPhotoUrl);
        this._registerShortcutKeyEvents();
    };
    PhotoViewer.prototype.hideViewerPanel = function () {
        this.viewerPanel.hide();
        this._unregisterShortcutKeyEvents();
    };
    PhotoViewer.prototype.showLastPhoto = function () {
        if (this.currentPhotoIndex <= 0) {
            this.tip.info("已经是第一张图了");
            return;
        }
        this.currentPhotoIndex -= 1;
        var currentPhoto = this.photoDoms[this.currentPhotoIndex];
        var viewPhotoUrl = this.config.getViewPhotoUrl.apply(this, [
            currentPhoto,
        ]);
        this._setViewPhoto(viewPhotoUrl);
    };
    PhotoViewer.prototype.showNextPhoto = function () {
        if (this.currentPhotoIndex >= this.photoDoms.length - 1) {
            this.tip.info("已经是最后一张图了");
            return;
        }
        this.currentPhotoIndex += 1;
        var currentPhoto = this.photoDoms[this.currentPhotoIndex];
        var viewPhotoUrl = this.config.getViewPhotoUrl.apply(this, [
            currentPhoto,
        ]);
        this._setViewPhoto(viewPhotoUrl);
    };
    /**
     * 使图片自适应并居中显示
     * 规则 1 : 如果图片原始大小小于屏幕宽高，用原始的宽高显示
     * 规则 2 : 如果图片原始大小超出屏幕范围，则缩放至屏幕内可以显示的程度
     */
    PhotoViewer.prototype.scaleAutoPhoto = function () {
        var self = this;
        var $photo = this.viewerPanel.find(".current-photo");
        var photo = $photo.get(0);
        var windowWidth = window.innerWidth;
        var windowHeight = window.innerHeight;
        //流出部分空白，显示会更美观点
        var maxWidth = windowWidth - 200;
        var maxHeight = windowHeight - 200;
        var naturalWidth = photo.naturalWidth;
        var naturalHeight = photo.naturalHeight;

        var newWidth = 0;
        var newHeight = 0;
        /**
         * 容器能完全显示图片则不进行处理
         */
        if (naturalWidth < maxWidth && naturalHeight < maxHeight) {
            newWidth = naturalWidth;
            newHeight = naturalHeight;
        } else {
            var widthRate = maxWidth / naturalWidth;
            var heightRate = maxHeight / naturalHeight;
            //以容器和图片的宽高比例中【最小】的一个进行计算
            var rate = widthRate < heightRate ? widthRate : heightRate;
            newWidth = Math.floor(naturalWidth * rate);
            newHeight = Math.floor(naturalHeight * rate);
        }
        $photo.fadeIn(1000);
        $photo.css({ width: newWidth + "px", height: newHeight + "px" });
        /*  $photo.animate(
            {
                opacity: "1",
                width: newWidth + "px",
                height: newHeight + "px",
            },
            "fast"
        );
        */
        self.setPhotoPositionCenter(newWidth, newHeight);
        self.hasDrag = false;
    };
    PhotoViewer.prototype.scaleUpPhoto = function () {
        var $photo = this.viewerPanel.find(".current-photo");
        var rate = 1 + this.config.scaleRate;
        var newWidth = Math.ceil($photo.width() * rate);
        var newHeight = Math.ceil($photo.height() * rate);
        //最多放大10倍
        var maxWidth = $photo.get(0).naturalWidth * 10;
        var maxHeight = $photo.get(0).naturalHeight * 10;
        if (newWidth > maxWidth || newHeight > maxHeight) {
            this.tip.info("图片已经很大了");
            return;
        }
        this.setPhotoSize(newWidth, newHeight);
        if (!this.hasDrag) {
            this.setPhotoPositionCenter(newWidth, newHeight);
        }
    };
    PhotoViewer.prototype.scaleDownPhoto = function () {
        var photo = this.viewerPanel.find(".current-photo");
        var rate = 1 - this.config.scaleRate;
        var newWidth = Math.ceil(photo.width() * rate);
        var newHeight = Math.ceil(photo.height() * rate);

        //最多缩小10倍
        var minWidth = Math.ceil(photo.get(0).naturalWidth / 10);
        var minHeight = Math.ceil(photo.get(0).naturalHeight / 10);
        if (window.innerWidth < minWidth) {
            minWidth = window.innerWidth - 200;
        }
        if (window.innerHeight < minHeight) {
            minHeight = window.innerHeight - 200;
        }

        if (newWidth < minWidth || newHeight < minHeight) {
            this.tip.info("图片已经很小了");
            return;
        }
        this.setPhotoSize(newWidth, newHeight);
        if (!this.hasDrag) {
            this.setPhotoPositionCenter(newWidth, newHeight);
        }
    };

    /**
     * 设置图片大小
     * @param {Number} width
     * @param {Number} height
     */
    PhotoViewer.prototype.setPhotoSize = function (width, height) {
        var $photo = this.viewerPanel.find(".current-photo");
        $photo.css({
            width: width + "px",
            height: height + "px",
        });
    };

    /**
     * 设置图片位置
     * @param {Number} left
     * @param {Number} top
     */
    PhotoViewer.prototype.setPhotoPosition = function (left, top) {
        var $photoContainer = this.viewerPanel.find(".photo-container");
        $photoContainer.css({
            left: left + "px",
            top: top + "px",
        });
    };

    /**
     * 设置图片居中显示
     */
    PhotoViewer.prototype.setPhotoPositionCenter = function (
        photoWidth,
        photoHeight
    ) {
        var $photo = this.viewerPanel.find(".current-photo");
        var windowWidth = window.innerWidth;
        var windowHeight = window.innerHeight;
        if (photoWidth === undefined || photoWidth === null) {
            photoWidth = $photo.innerWidth();
        }
        if (photoHeight === undefined || photoHeight === null) {
            photoHeight = $photo.innerHeight();
        }
        var left = Math.floor((windowWidth - photoWidth) / 2);
        var top = Math.floor((windowHeight - photoHeight) / 2);
        this.setPhotoPosition(left, top);
    };
    /**
     * 销毁对象的方法
     */
    PhotoViewer.prototype.destory = function () {
        //清除构造函数上的实例存储
        var newInstances = [];
        var oldInstances = this.constructor.instances;
        for (var i = 0; i < oldInstances.length; i++) {
            var instance = oldInstances[i];
            if (instance.id !== this.id) {
                newInstances.push(instance);
            }
        }
        this.constructor.instances = newInstances;
        this.photoDoms.off("click");
        this.photoDoms = [];
        this.rootDom = null;
        if (this.viewerPanel) {
            this.viewerPanel.remove();
            this.viewerPanel = null;
        }
    };
    PhotoViewer.prototype._initViewerPanel = function () {
        //根容器dom
        this.viewerPanel = $(
            '<div id="photo-viewer-' + this.id + '" class="photo-viewer"></div>'
        );
        //遮罩层
        var $mask = $('<div class="mask"><div>');
        $mask.css({
            "background-color": this.config.maskColor,
        });
        this.viewerPanel.append($mask.get(0));
        //关闭按钮
        this.viewerPanel.append(
            $('<div class="icon icon-close btn btn-close"><div>').get(0)
        );
        //上一张、下一张按钮
        this.viewerPanel.append(
            $(
                '<div class="btn btn-last"><div class="icon icon-last"></div></div>'
            ).get(0)
        );
        this.viewerPanel.append(
            $(
                '<div class="btn btn-next"><div class="icon icon-next"></div></div>'
            ).get(0)
        );
        //工具栏按钮
        this.viewerPanel.append(
            $(
                '<div class="tool-bar">' +
                    '<div class="btn btn-tool btn-scale-up"><div class="icon icon-scale-up"></div></div>' +
                    '<div class="btn btn-tool btn-scale-down"><div class="icon icon-scale-down"></div></div>' +
                    '<div class="btn btn-tool btn-scale-auto"><div class="icon icon-scale-auto"></div></div>'
            ).get(0)
        );
        //图片显示容器
        this.viewerPanel.append(
            $(
                '<div class="photo-container"><img class="current-photo"/></div>'
            ).get(0)
        );
        this._setBtnDescText();
        document.body.append(this.viewerPanel.get(0));
        this._registerViewerPanelEvents();
    };
    /**
     *
     */
    PhotoViewer.prototype._setBtnDescText = function () {
        this.viewerPanel.find(".btn-close").prop("title", "退出");
        this.viewerPanel.find(".btn-last").prop("title", "上一张图");
        this.viewerPanel.find(".btn-next").prop("title", "下一张图");
        this.viewerPanel.find(".btn-scale-up").prop("title", "放大");
        this.viewerPanel.find(".btn-scale-down").prop("title", "缩小");
        this.viewerPanel.find(".btn-scale-auto").prop("title", "自适应");
    };
    PhotoViewer.prototype._registerViewerPanelEvents = function () {
        var self = this;
        if (self.config.clickMaskHide) {
            self.viewerPanel
                .find(".mask")
                .off("click")
                .on("click", function (event) {
                    event.stopPropagation();
                    self.hideViewerPanel();
                });
        }
        self.viewerPanel
            .find(".btn-close")
            .off("click")
            .on("click", function (event) {
                event.stopPropagation();
                self.hideViewerPanel();
            });
        self.viewerPanel
            .find(".btn-last")
            .off("click")
            .on("click", function (event) {
                event.stopPropagation();
                self.showLastPhoto();
            });
        self.viewerPanel
            .find(".btn-next")
            .off("click")
            .on("click", function (event) {
                event.stopPropagation();
                self.showNextPhoto();
            });
        self.viewerPanel
            .find(".btn-scale-up")
            .off("click")
            .on("click", function (event) {
                event.stopPropagation();
                self.scaleUpPhoto();
            });
        self.viewerPanel
            .find(".btn-scale-down")
            .off("click")
            .on("click", function (event) {
                event.stopPropagation();
                self.scaleDownPhoto();
            });
        self.viewerPanel
            .find(".btn-scale-auto")
            .off("click")
            .on("click", function (event) {
                event.stopPropagation();
                self.scaleAutoPhoto();
            });
        self.viewerPanel
            .find(".current-photo")
            .off("load")
            .on("load", function (event) {
                event.stopPropagation();
                self.scaleAutoPhoto();
            });
        var $photoContainer = self.viewerPanel.find(".photo-container");
        $photoContainer
            .off("mousedown")
            .on("mousedown", function (mousedownEvent) {
                mousedownEvent.preventDefault();
                self.hasDrag = true;
                /**鼠标点击图片的位置和图片左上角的x偏移量 */
                //浏览器内容区域左上角 - 拖拽dom对象的 left top
                var offsetX =
                    mousedownEvent.clientX -
                    parseInt($photoContainer.css("left"));
                /**鼠标点击图片的位置和图片左上角的y偏移量 */
                var offsetY =
                    mousedownEvent.clientY -
                    parseInt($photoContainer.css("top"));

                var mousemoveHandler = function (event) {
                    event.stopPropagation();
                    var clientX = event.clientX;
                    var clientY = event.clientY;
                    // 限定 x 的范围
                    if (clientX < 80) {
                        clientX = 80;
                    }
                    if (clientX > window.innerWidth - 80) {
                        clientX = window.innerWidth - 80;
                    }
                    //限定 y 的范围
                    if (clientY < 80) {
                        clientY = 80;
                    }
                    if (clientY > window.innerHeight - 80) {
                        clientY = window.innerHeight - 80;
                    }
                    event.preventDefault();
                    var left = clientX - offsetX;
                    var top = clientY - offsetY;
                    self.setPhotoPosition(left, top);
                };
                var mouseupHandler = function (event) {
                    event.stopPropagation();
                    event.preventDefault();
                    self.viewerPanel.off("mousemove");
                    self.viewerPanel.off("mouseup");
                };
                //点击的时候才注册事件
                self.viewerPanel.on(
                    "mousemove",
                    throttle(mousemoveHandler, 50)
                );
                self.viewerPanel.on("mouseup", mouseupHandler);
            });
    };
    /** 注册快捷键事件，快捷键和 ui上的事件不同，相关ui没了，相关的快捷键事件也要注销 */
    PhotoViewer.prototype._registerShortcutKeyEvents = function () {
        var self = this;
        //加上节流器
        var thr_mousewheelHandler = throttle(self._mousewheelHandler, 100);
        self._handleMousewheel = function (event) {
            event.stopPropagation();
            event.preventDefault();
            thr_mousewheelHandler.apply(self, [event]);
        };
        //鼠标滚轮滚动进行图片滑动,放大/缩小
        window.addEventListener("mousewheel", self._handleMousewheel, {
            passive: false,
        });

        //firefox
        window.addEventListener("DOMMouseScroll", self._handleMousewheel, {
            passive: false,
        });
        var debScaleAutoPhoto=debounce(function () {
            self.scaleAutoPhoto();
        }, 100)
        //窗口大小发生变化的时候，重新计算图片自适应大小
        $(window).on(
            "resize.photo-viewer-" + self.id,
            function(event) {
                event.stopPropagation();
                debScaleAutoPhoto();
            }
        );
        $(window).on("keydown.photo-viewer-" + self.id, function (event) {
            event.stopPropagation();
            var $photoContainer = self.viewerPanel.find(".photo-container");
            var keyCode = event.keyCode;
            // var ctrlKey = event.ctrlKey; //是否按住了 ctrl 键
            switch (keyCode) {
                case keyCodeEnum.leftArrow:
                    self.showLastPhoto();
                    break;
                case keyCodeEnum.rightArrow:
                    self.showNextPhoto();
                    break;
                case keyCodeEnum.upArrow:
                    var offsetY = 10;
                    var newTop = parseInt($photoContainer.css("top")) + offsetY;
                    $photoContainer.css({
                        top: newTop,
                    });
                    break;
                case keyCodeEnum.downArrow:
                    var offsetY = -10;
                    var newTop = parseInt($photoContainer.css("top")) + offsetY;
                    $photoContainer.css({
                        top: newTop,
                    });
                    break;
            }
        });
    };
    /** 注销快捷键事件*/
    PhotoViewer.prototype._unregisterShortcutKeyEvents = function () {
        var self = this;
        window.removeEventListener("mousewheel", self._handleMousewheel, {
            passive: false,
        });
        window.removeEventListener("DOMMouseScroll", self._handleMousewheel, {
            passive: false,
        });
        $(window).off("resize.photo-viewer-" + self.id);
        $(window).off("keydown.photo-viewer-" + self.id);
    };
    PhotoViewer.prototype._handleMousewheel = function () {};
    /**
     * 鼠标滚动行为处理函数，因为 ctrl + 鼠标滚动比较特殊，只能写原生
     * 主要兼容chrome
     * @param {*} event
     */
    PhotoViewer.prototype._mousewheelHandler = function (event) {
        event.stopPropagation(); 
        var self = this;
        //滚轮滚动的角度数，正数为向上，负数为向下滚动
        var delta = event.wheelDelta || event.wheelDeltaY || event.detail;/*火狐浏览器没有 wheelDelta */
        var deltaCount = delta / 120; //滚动一格的值是120
        event.preventDefault();
        if (deltaCount > 0) {
            self.scaleUpPhoto();
        } else {
            self.scaleDownPhoto();
        }
    };
    PhotoViewer.prototype._setViewPhoto = function (viewPhotoUrl) {
        var $photo = this.viewerPanel.find(".current-photo");
        // $photo.css("opacity", "0");
        $photo.fadeOut(1000);
        //停止预留动画
        $photo.stop(true, true);
        $photo.prop("src", viewPhotoUrl);
    };
    PhotoViewer.prototype.tip = {
        info: function (msg) {
            alert(msg);
        },
    };
    /**
     *
     * @param {*} num 当前数
     * @param {*} total 总数
     */
    $.fn.extend({
        photoViewer: function (config) {
            var rootDom = this;
            return new PhotoViewer(config, rootDom);
        },
    });
    /**内部实例的构造函数 */
    $.fn.photoViewer.innerCtor = PhotoViewer;
})(jQuery);
